package view;

import test.Config;
import test.Test;
import view.drawable.IDrawable;
import view.listeners.NonOverlapMoveAdapter;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class JCanvas extends JPanel {

    private List<IDrawable> drawables = new LinkedList<IDrawable>();
    private double scale = 1;
    private double translate_x = 0;
    private double translate_y = 0;
    private double translate_scale_x = 0;
    private double translate_scale_y = 0;
    private double center_scale_x = 0;
    private double center_scale_y = 0;

    private BufferedImage fond;

    public JCanvas() {
        //Listeners
        new NonOverlapMoveAdapter(this);
        try {
            fond = ImageIO.read(new File("./ressources/fond_paris.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @pre : d != null
     * @post : le drawable d est ajout� sur this
     */
    public void addDrawable(IDrawable d) {
        drawables.add(d);
        repaint();
    }

    /**
     * @pre:d!= null
     * @post: d est retir� de this
     */
    public void removeDrawable(IDrawable d) {
        drawables.remove(d);
        repaint();
    }

    public void paint(Graphics g) {
        super.paint(g);

        Graphics2D graphics2D = (Graphics2D)g;

        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // Anti-alias!
                RenderingHints.VALUE_ANTIALIAS_ON);

        graphics2D.setColor(new Color(0, 42, 54));
        Rectangle rectangle = graphics2D.getClipBounds();
        graphics2D.fillRect(rectangle.x,rectangle.y,rectangle.width,rectangle.height);

        AffineTransform old=graphics2D.getTransform();
        graphics2D.translate(this.getCenter_scale_x(), this.getCenter_scale_y());
        graphics2D.scale(this.getScale(), this.getScale());
        graphics2D.translate(this.getTranslate_scale_x(), this.getTranslate_scale_y());
        graphics2D.translate(this.getTranslate_x(), this.getTranslate_y());

        if (Test.getGraphFile() == Config.GraphFile.METRO_PARIS) {
            AffineTransform affineTransform = new AffineTransform();
            affineTransform.scale(2,2);
            affineTransform.translate(2750, 0);
            AffineTransformOp affineTransformOp = new AffineTransformOp(affineTransform,AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
            ((Graphics2D) g).drawImage(fond, affineTransformOp, 0, 0);
        }

        for (Object drawable : drawables) {
            ((IDrawable) drawable).draw(g);
        }
    }

    public void clear() {
        drawables.clear();
        repaint();
    }

    public Point transformPoint(Point p) {
        Point ret = new Point(p.x,p.y);

        ret.translate((int) this.getCenter_scale_x(), (int) this.getCenter_scale_y());
        ret.setLocation(ret.x * this.getScale(), ret.y * this.getScale());
        ret.translate((int) this.getTranslate_scale_x(), (int) this.getTranslate_scale_y());
        ret.translate((int) this.getTranslate_x(), (int) this.getTranslate_y());

        return ret;
    }

    public Point transformInversePoint(Point p) {
        Point ret = new Point(p.x,p.y);

        ret.translate((int) -this.getCenter_scale_x(), (int) -this.getCenter_scale_y());
        ret.setLocation(ret.x / this.getScale(), ret.y / this.getScale());
        ret.translate((int) -this.getTranslate_scale_x(), (int) -this.getTranslate_scale_y());
        ret.translate((int) -this.getTranslate_x(), (int) -this.getTranslate_y());

        return ret;
    }

    public List findDrawables(Point p) {
        List l = new ArrayList();
        for (Object drawable : drawables) {
            IDrawable element = (IDrawable) drawable;
            if (element.getRectangle().contains(p)) {
                l.add(element);
            }
        }
        return l;
    }

    public boolean isFree(Rectangle rect) {
        for (Object drawable : drawables) {
            IDrawable element = (IDrawable) drawable;
            if (element.getRectangle().intersects(rect)) {
                return false;
            }
        }
        return true;
    }

    public boolean isAlone(IDrawable drawable) {
        Rectangle rect = drawable.getRectangle();
        for (Object drawable1 : drawables) {
            IDrawable element = (IDrawable) drawable1;
            if (!element.equals(drawable)
                    && element.getRectangle().intersects(rect)) {
                return false;
            }
        }
        return true;
    }

    public double getTranslate_x() {
        return translate_x;
    }

    public void setTranslate_x(double translate_x) {
        this.translate_x = translate_x;
    }

    public double getTranslate_y() {
        return translate_y;
    }

    public void setTranslate_y(double translate_y) {
        this.translate_y = translate_y;
    }

    public double getCenter_scale_x() {
        return center_scale_x;
    }

    public void setCenter_scale_x(double center_scale_x) {
        this.center_scale_x = center_scale_x;
    }

    public double getCenter_scale_y() {
        return center_scale_y;
    }

    public void setCenter_scale_y(double center_scale_y) {
        this.center_scale_y = center_scale_y;
    }

    public double getScale() {
        return scale;
    }

    public void setScale(double scale) {
        this.scale = scale;
    }

    public double getTranslate_scale_x() {
        return translate_scale_x;
    }

    public void setTranslate_scale_x(double translate_scale_x) {
        this.translate_scale_x = translate_scale_x;
    }

    public double getTranslate_scale_y() {
        return translate_scale_y;
    }

    public void setTranslate_scale_y(double translate_scale_y) {
        this.translate_scale_y = translate_scale_y;
    }
}